/**
 * GeneralSystemsNetManager.java
 * ************************** 
 * @date Apr 18, 2011
 * @author Michele Tamburini
 * 
 * **************************
 */
package systems.network;

import infrastructureAPI.backendGate.scriptsResponse.ResponseScripAPIRemote;

import java.net.InetAddress;
import java.rmi.AccessException;
import java.rmi.NotBoundException;
import java.rmi.RemoteException;
import java.rmi.registry.LocateRegistry;
import java.rmi.registry.Registry;
import java.util.ArrayDeque;
import java.util.Queue;

import networkRmi.NetworkManager;

import messages.Message;
import messages.MessageHandler;

/**
 * Description: 
 * This class is an implementation of the {@link NetworkSystemsAPI} and so
 * it gives the access to the network with the possibility to contact other nodes.
 * It's also a {@link MessageHandler} and it maintains a queue of the 
 * received messages for the specified system.
 * It's an abstract class because it not explicitly define a function
 * to extract the queue of incoming messages from the {@link NetworkManager}.
 * Each system must provide its own method to extract such messages
 * from the incoming queue and collect them into the receivedSystemMessages.
 * 
 */
public abstract class GeneralSystemsNetManager implements MessageHandler,
		NetworkSystemsAPI {
	
	/**
	 * If true activate a verbose mode useful for the debug.
	 */
	protected static boolean DEBUG_PRINT = false;
	
	/**
	 * If true shows the errors such as remote errors, 
	 * connection, unreliability ...
	 */
	protected static boolean SHOW_SYSTEMS_NETMAN_ERRORS = true;
	
	protected NetworkManager nm;
	protected static int QUEUE_SIZE = 100;

	/**
	 * The queue of Message(s) received into this network manager. 
	 * Note that it contains informations, warnings and all sorts
	 * of communication Message(s) from systems in the other nodes,
	 * as well as the requests coming from the Front-End.
	 * 
	 */
	protected Queue<Message> receivedSystemMessages;
	
	/**
	 * 
	 */
	public GeneralSystemsNetManager(NetworkManager netMan) {
		this.nm = netMan;
		receivedSystemMessages = new ArrayDeque<Message>(QUEUE_SIZE);
		nm.registerService(this);
	}

	@Override
	public abstract void extractIncomingMessages(Queue<Message> queue);
	
	@Override
	public boolean send(Message msg){
		boolean ret = false;
		try {
			nm.send(msg, msg.getDest().getNodeAddress());
			ret = true;
		} catch (RemoteException e) {
			if(SHOW_SYSTEMS_NETMAN_ERRORS)
				System.err.println(this.toString()+" "+
						" problem sending "+ msg +" to "+msg.getDest()+
						" thrown a RemoteException");
			e.printStackTrace();
		} catch (NotBoundException e) {
			if(SHOW_SYSTEMS_NETMAN_ERRORS)
				System.err.println(this.toString()+
						" problem sending "+ msg +" to "+msg.getDest()+
						" exception is "+ e);
			e.printStackTrace();
		}
		return ret;
		
	}
	
	@Override
	public Message receive(){
		Message ret = null;
//		String thread = Thread.currentThread().getName();
		if(!receivedSystemMessages.isEmpty()){
			synchronized (receivedSystemMessages) {
				ret = receivedSystemMessages.poll();
			}			
//			System.out.println(thread +" estracted immidiatly "+ret);
		}
		//blocking function
		while(ret == null){
			try {
				synchronized (receivedSystemMessages) {
//					System.out.println(thread+" waiting on receivedSystemMessages");
					receivedSystemMessages.wait(0);
					if(!receivedSystemMessages.isEmpty()){
						ret = receivedSystemMessages.poll();
					}
					receivedSystemMessages.notify();
				}
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
		}
		
		
		return ret;
	}
	
	@Override 
	public boolean contactRemoteService(InetAddress address, Integer port, String service,
			Message msg) throws RemoteException, NotBoundException{
		boolean ret = false;		
		Registry registry;
		
//		if(DEBUG_PRINT)
//			System.out.println("get registry on "+address.getHostAddress()+":"+port);
		registry = LocateRegistry.getRegistry(address.getHostAddress(), port);
//		if(DEBUG_PRINT)
//			printRegistry(registry);
		ResponseScripAPIRemote responseAPI = (ResponseScripAPIRemote) registry.lookup(service);
//		if(DEBUG_PRINT)
//			System.out.println("looking for: "+service+"\nfound service: "+responseAPI);
		responseAPI.depositResponse(msg);
		
		return ret;
	}
	
	protected void printRegistry(Registry registry){
		try {
			String registered[] = registry.list();
			System.out.println("Services registered in "+
					registry);
			for(String s : registered){
				System.out.println("-> "+s);
			}
		} catch (AccessException e) {
			e.printStackTrace();
		} catch (RemoteException e) {
			e.printStackTrace();
		}
	}
	
	@Override
	public Class<?> getServiceClass(){
		return NetworkSystemsAPI.class;
	}

}
